/**
 * 异常及调试
 * @param key string 异常KEY  约定格式:"{src}.{KEY}"
 * 		前缀'@': 外部异常, 默认非前缀'@':系统异常
 * @param val mixed|json|null 异常信息  根据opt.sys:
 * 		true: mixed|null 异常实例/可序列化数据 日志/打印
 * 			$.err()实例: 不做任何处理, 仅根据opt.rtn处理
 * 			异常实例: opt.fix=string > Error.stack
 * 				opt.fix=int -> 以stack忽略最近opt.fix个
 * 		false: json|null 语言模板变量  无处理,仅记录传递
 * @param opt json|boolean 选项  boolean等同:{rtn:opt}
 * 		fix: int|string [调试]堆栈跟踪  默认:0
 * 			int: 取当前,忽略最近fix个  0:调用$.err()位置
 * 			string: "*(\n\s*at *)+\s*"  前*任意,内部删除
 * 		rtn: boolean 是否返回  默认:false -> 处理后,抛出
 * 		ext: mixed|null 额外数据 仅传递,无处理 默认:null
 * @return this 在opt.rtn=true时,返回实例
 */
$.err = function(key, val=null, opt={}){
	if(typeof opt==='boolean') opt = {rtn:opt};
	if(val instanceof $.err){ if(opt.rtn) return val; val.thr(); }
	if(!(this instanceof $.err)){
		let isErrObj=val instanceof Error, fix=opt.fix??0, e;
		if((+fix)===fix) opt.fix = isErrObj ? fix : (fix+1);
		e=new $.err(key,val,opt);  if(opt.rtn) return e;  e.thr();
	}
	const sys=key[0]!=='@';  if(!sys) key=key.slice(1);
	let {fix,ext}=opt, N=(+fix)===fix, E=val instanceof Error, msg;
	let n=N?fix:0;  if(E){ fix=N?val.stack:fix; val=val.message; }
	fix=$.stack(N?(E?n:n+1):(fix?n:1),E||!N?fix:null);  msg=null;
	if(sys) msg=$.txt(val);  const v={sys,key,val,msg,fix,ext};
	Object.defineProperty(this,'__err',{value:v,enumerable:false});
	const s = `\x1B[1;31m[${sys?'ERROR':'FAIL'}]\x1B[0m `;
	this.stack=s+this.txt(true,true);  return this;
};
$.err.prototype=Object.assign(Object.create(Error.prototype),{
	constructor: $.err,
	/**
	 * 取得异常信息
	 * @param k string|null 取得KEY  null:取全部->json
	 * 		sys: boolean 是否系统异常  false:外部异常
	 * 		key: string 异常KEY  不含@前缀 $.err().key
	 * 		val: json|mixed|null 异常信息 $ .err().val
	 * 			sys ? mixed|null : json|null
	 * 		msg: string|null 系统异常文本  val->string
	 * 			仅系统异常, 外部异常:null,未取语言信息
	 * 		fix: string 堆栈信息  以\n拼接'    at *'
	 * 		ext: mixed|null 额外数据 $.err().opt.ext
	 * @return json|boolean|string|json|mixed|null
	 */
	get(k=null){ return k?(this.__err[k]||null):this.__err; },
	/**
	 * 取得异常文本
	 * @param fix boolean 是否含堆栈信息
	 * @param log boolean 是否为打印作色
	 * @return string @?key+msg|val+?fix
	 */
	txt(fix=false, log=false){
		const T=this.__err, KEY=(T.sys?'':'@')+T.key;
		let v = log ? `\x1B[31m${KEY}\x1B[0m` : KEY;
		v += T.sys ? T.msg : ' '+$.lang(T.key,T.val);
		if(T.ext){
			const k = log?`\x1B[36mdata\x1B[0m`:'data';
			v += '\n  '+k+':'+$.txt(T.ext);
		}
		if(!log) return fix ? v+'\n  file:'+T.fix : v;
		return v+(fix?'  \n\x1B[36m  file\x1B[0m:'+T.fix:'');
	},
	/**
	 * 日志: 打印+文件(NODE)
	 * @param type string 日志类型 $.log().type
	 * 		'': 根据sys -> 系统异常 ? ERROR : FAIL
	 * @param save object|null [node]处理实例 $.srvHandle()
	 */
	log(type='', save=null){
		if(!type) type=this.__err.sys?'ERROR':'FAIL';
		if(!save) $.log(this.txt(true,true), type);
		else save.log(this.txt(true,true), type);
	},
	/**
	 * 异常抛出
	 */
	thr(){ throw this; }, //$.err()
});
